package emailclient;

import java.awt.*;
import java.awt.event.*;
import java.net.*;
import java.util.*;
import javax.mail.*;
import javax.mail.event.*;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.swing.*;
import javax.swing.event.*;

/**
 * @class EmailClient
 * @brief почтовый клиент.
 * 
 * Данный класс организуте работу с почтовыми серверами POP3 и SMTP
 * @author Kazakov A.V.
 * @version 1.0
 */
public class EmailClient extends JFrame
{
	/**
	 * модельтаблицы данных сообщений
	 */
	private MessagesTableModel tableModel;

	/**
	 * таблица со списком сообщений
	 */
	private JTable table;

	/**
	 * текстовое поле для отображения сообщений
	 */
	private JTextArea messageTextArea;

	/**
	 * панель разделенная на таблицу сообщений и панель для отображения
	 * содержимого сообщения
	 */
	private JSplitPane splitPane;

	/**
	 * кнопка ответа на сообщение
	 */
	private JButton replyButton;

	/**
	 * кнопка пересылки сообщения
	 */
	private JButton forwardButton;

	/**
	 * кнопка удаления сообщения
	 */
	private JButton deleteButton;

	/**
	 * Выбранное из таблицы сообщение
	 */
	private Message selectedMessage;

	/**
	 * флаг для отображения состояния сообщения
	 */
	private boolean deleting;

	/**
	 * Сеанс JavaMail
	 */
	private Session session;

	/**
	 * конструктор почтового клиента
	 */
	public EmailClient()
	{
		//	установить заголовок приложения
		setTitle("E-mail Client");

		//	установить размеры окна
		setSize(640, 480);

		//	обработать событие при закрытии окна
		addWindowListener(new WindowAdapter()
		{
			@Override
			public void windowClosing(WindowEvent e)
			{
				actionExit();
			}
		});

		//	установить меню файлов
		JMenuBar menuBar = new JMenuBar();
		JMenu fileMenu = new JMenu("File");
		fileMenu.setMnemonic(KeyEvent.VK_F);
		JMenuItem fileExitMenuItem = new JMenuItem("E&xit", KeyEvent.VK_X);
		fileExitMenuItem.addActionListener(new ActionListener()
		{
			@Override
			public void actionPerformed(ActionEvent e)
			{
				actionExit();
			}
		});
		fileMenu.add(fileExitMenuItem);
		menuBar.add(fileMenu);
		setJMenuBar(menuBar);

		//	установить панель кнопок
		JPanel buttonPanel = new JPanel();
		JButton newButton = new JButton("New Message");
		newButton.addActionListener(new ActionListener()
		{
			@Override
			public void actionPerformed(ActionEvent e)
			{
				actionNew();
			}
		});
		buttonPanel.add(newButton);

		//	установить таблицу сообщений
		tableModel = new MessagesTableModel();
		table = new JTable(tableModel);
		table.getSelectionModel().addListSelectionListener(
				new ListSelectionListener()
		{
			@Override
			public void valueChanged(ListSelectionEvent e)
			{
				tableSelectionChanged();
			}
		});
		//	разрешить выбирать только одну строку
		table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

		// уствноыить панель почтового клиента
		JPanel emailsPanel = new JPanel();
		emailsPanel.setBorder(BorderFactory.createTitledBorder("E-mails"));

		messageTextArea = new JTextArea();
		messageTextArea.setEditable(false);
		splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, 
				new JScrollPane(table), 
				new JScrollPane(messageTextArea));
		emailsPanel.setLayout(new BorderLayout());
		emailsPanel.add(splitPane, BorderLayout.CENTER);

		//	установить вторую панель для кнопок
		JPanel buttonPanel2 = new JPanel();

		replyButton = new JButton("Reply");
		replyButton.addActionListener(new ActionListener()
		{
			@Override
			public void actionPerformed(ActionEvent e)
			{
				actionReply();
			}
		});
		replyButton.setEnabled(false);
		buttonPanel2.add(replyButton);

		forwardButton = new JButton("Forward");
		forwardButton.addActionListener(new ActionListener()
		{
			@Override
			public void actionPerformed(ActionEvent e)
			{
				actionForward();
			}
		});
		forwardButton.setEnabled(false);
		buttonPanel2.add(forwardButton);

		deleteButton = new JButton("Delete");
		deleteButton.addActionListener(new ActionListener()
		{
			@Override
			public void actionPerformed(ActionEvent e)
			{
				actionDelete();
			}
		});
		deleteButton.setEnabled(false);
		buttonPanel2.add(deleteButton);

		getContentPane().setLayout(new BorderLayout());
		getContentPane().add(buttonPanel, BorderLayout.NORTH);
		getContentPane().add(emailsPanel, BorderLayout.CENTER);
		getContentPane().add(buttonPanel2, BorderLayout.SOUTH);
	}

	/**
	 * выход из программы
	 */
	private void actionExit()
	{
		System.exit(0);
	}

	/**
	 * создать новое сообщение
	 */
	private void actionNew()
	{
		sendMessage(MessageDialog.NEW, null);
	}

	/**
	 * вызывается при изменении выбранной строки таблицы
	 */
	private void tableSelectionChanged()
	{
		//	если не в удалленных сообщениях, установить выбранное сообщение и
		//	показать его
		if(!deleting)
		{
			selectedMessage = tableModel.getMessage(table.getSelectedRow());
			showSelectedMessage();
			updateButtons();
		}
	}

	/**
	 * ответить на сообщение
	 */
	private void actionReply()
	{
		sendMessage(MessageDialog.REPLY, selectedMessage);
	}

	/**
	 * перенаправить сообщение
	 */
	private void actionForward()
	{
		sendMessage(MessageDialog.FORWARD, selectedMessage);
	}

	/**
	 * удалитьвыбранное сообщение
	 */
	private void actionDelete()
	{
		deleting = true;
		try
		{
			//	удалить сообщение с сервера
			selectedMessage.setFlag(Flags.Flag.DELETED, true);
			Folder folder = selectedMessage.getFolder();
			folder.close(true);
			folder.open(Folder.READ_WRITE);
		}
		catch(Exception e)
		{
			showError("Unable to delete message.", false);
		}

		//	удалить сообщения из таблицы
		tableModel.deleteMessage(table.getSelectedRow());

		//	обновить GUI
		messageTextArea.setText("");
		deleting = false;
		selectedMessage = null;
		updateButtons();
	}

	/**
	 * отправить указанное сообщение
	 * @param type тип сообщения
	 * @param message сообщение
	 */
	private void sendMessage(int type, Message message)
	{
		// показать диалог сообщения для получения параметров отправки сообщения
		MessageDialog dialog;
		try
		{
			dialog = new MessageDialog(this, type, message);
			if(!dialog.display())
			{
				//	возврат при отмене диалога
				return;
			}
		}
		catch(Exception e)
		{
			showError("Unable to send message.", false);
			return;
		}

		try
		{
			//	создать новое сообщение со значениями из диалогового окна
			Message newMessage = new MimeMessage(session);
			newMessage.setFrom(new InternetAddress(dialog.getFrom()));
			newMessage.setRecipient(Message.RecipientType.TO, 
					new InternetAddress(dialog.getTo()));
			newMessage.setSubject(dialog.getSubject());
			newMessage.setSentDate(new Date());
			newMessage.setText(dialog.getContent());

			//	отправтить новое сообщение
			Transport.send(newMessage);
		}
		catch(Exception ex)
		{
			showError("Unable to send message.", false);
		}
	}

	/**
	 * отобразить выбранное сообщение в панели длч содержимого
	 */
	private void showSelectedMessage()
	{
		// показать песочные часы, пока сообщение отображается
		setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));

		try
		{
			messageTextArea.setText(getMessageContent(selectedMessage));
			messageTextArea.setCaretPosition(0);
		}
		catch(Exception e)
		{
			showError("Unabled to load message.", false);
		}
		finally
		{
			setCursor(Cursor.getDefaultCursor());
		}
	}

	/**
	 * обновить состояние каждой кнопки, на основе того, выбрано ли сообщение 
	 * в таблице.
	 */
	private void updateButtons()
	{
		if(selectedMessage != null)
		{
			replyButton.setEnabled(true);
			forwardButton.setEnabled(true);
			deleteButton.setEnabled(true);
		}
		else
		{
			replyButton.setEnabled(true);
			forwardButton.setEnabled(true);
			deleteButton.setEnabled(true);
		}
	}

	/**
	 * показатьокно приложения на экране
	 */
	@Override
	public void show()
	{
		super.show();

		//	обновить разделение панели с соотношением 50/50
		splitPane.setDividerLocation(.5);
	}

	/**
	 * подключиться к почтовому серверу
	 */
	public void connect()
	{
		//	отобразить диалоговое окно для соединения
		ConnectDialog dialog = new ConnectDialog(this);
		dialog.show();

		//	обработать данные для соединения
		StringBuffer connectionUrl = new StringBuffer();
		connectionUrl.append(dialog.getType() + "://");
		connectionUrl.append(dialog.getUsername() + ":");
		connectionUrl.append(dialog.getPassword() + "@");
		connectionUrl.append(dialog.getServer() + "/");

		//	отобразить диалоговое окно с фразой, что почтовые сообщения
		//	загружаются с сервера
		final DownloadingDialog downloadingDialog = new DownloadingDialog(this);
		SwingUtilities.invokeLater(new Runnable()
		{
			@Override
			public void run()
			{
				downloadingDialog.show();
			}
		});

		//	создать сеанс JavaMail и подключиться к серверу
		Store store = null;
		try
		{
			//	инициализировать сеанс JavaMail с SMTP-сервером
			Properties props = new Properties();
			props.put("mail.smtp.host", dialog.getSmtpServer());
			session = Session.getDefaultInstance(props, null);

			//	подключиться к почтовому серверу
			URLName urln = new URLName(connectionUrl.toString());
			store = session.getStore(urln);
			store.connect();
		}
		catch(Exception e)
		{
			//	закрытьдиалоговое окно
			downloadingDialog.dispose();

			//	отобразить диалоговое окно ошибки
			showError("Unable to connect.", true);
		}

		//	загрузить заголовки сообщений с сервера
		try
		{
			//	открыть каталог "INBOX"
			Folder folder = store.getFolder("INBOX");
			folder.open(Folder.READ_WRITE);

			//	получить список сообщений с сервера
			Message[] messages = folder.getMessages();
			
			//	извдечь заголовок каждого сообщения
			FetchProfile profile = new FetchProfile();
			profile.add(FetchProfile.Item.ENVELOPE);
			folder.fetch(messages, profile);

			//	переместить сообщения в таблицу
			tableModel.setMessages(messages);
		}
		catch(Exception e)
		{
			//	закрыть диалоговое окно
			downloadingDialog.dispose();

			//	отобразить диалоговое окно ошибки
			showError("Unable to download messages.", true);
		}

		//	закрыть диалоговое окно
		downloadingDialog.dispose();
	}

	/**
	 * отобразить диалоговое окно ошибки и выйти из программы
	 * @param message текст сообщения
	 * @param exit флаг выхода из программы
	 */
	private void showError(String message, boolean exit)
	{
		JOptionPane.showMessageDialog(this, 
				message, 
				"Error", 
				JOptionPane.ERROR_MESSAGE);
		if(exit)
			System.exit(0);
	}

	/**
	 * получить содержимое сообщения
	 * @param message сообщение
	 * @return текст сообщения
	 * @throws Exception 
	 */
	public static String getMessageContent(Message message)
			throws Exception
	{
		Object content = message.getContent();
		if(content instanceof Multipart)
		{
			StringBuffer messageContent = new StringBuffer();
			Multipart multipart = (Multipart)content;
			for(int i = 0; i < multipart.getCount(); i++)
			{
				Part part = (Part)multipart.getBodyPart(i);
				if(part.isMimeType("text/plain"))
				{
					messageContent.append(part.getContent().toString());
				}
			}

			return messageContent.toString();
		}
		else
		{
			return content.toString();
		}
	}

	public static void main(String[] args)
	{
		EmailClient client = new EmailClient();
		client.show();

		//	отобразить диалоговое окно для соединения
		client.connect();
	}
}
